﻿namespace Httlgd.DataCenter.Services.Core.Common
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Data.SqlTypes;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Text;
    using System.Text.RegularExpressions;    

    /// <summary>
    /// This class contains extension methods used through out system.
    /// </summary>
    public static class dotNetExtension
    {
        /// <summary>
        /// Converts string to integer. Returns 0 if invalid.
        /// </summary>
        /// <param name="s">string to convert</param>
        /// <returns></returns>
        public static int ToInt(this string s)
        {
            int defaultValue = 0;
            return s.ToInt(defaultValue);
        }

        /// <summary>
        /// Converts string to integer. Returns given defaultValue if invalid.
        /// </summary>
        /// <param name="s">string to convert</param>
        /// <param name="defaultValue">a default value</param>
        /// <returns></returns>
        public static int ToInt(this string s, int defaultValue)
        {
            int i = defaultValue;
            int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out i);

            return i;
        }

        /// <summary>
        /// Try Parse Long 
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static long ToLong(this string s)
        {
            long defaultValue = 0;
            return s.ToLong(defaultValue);
        }

        /// <summary>
        /// Try Parse Long
        /// </summary>
        /// <param name="s"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static long ToLong(this string s, long defaultValue)
        {
            long i = defaultValue;
            long.TryParse(s, out i);
            return i;
        }

        /// <summary>
        /// Try Parse Double 
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static double ToDoubleNumber(this string s)
        {
            double defaultValue = 0;
            return s.ToDoubleNumber(defaultValue);
        }

        /// <summary>
        /// Try Parse Double 
        /// </summary>
        /// <param name="s"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static double ToDoubleNumber(this string s, double defaultValue)
        {
            double i = defaultValue;
            double.TryParse(s, out i);

            return i;
        }

        /// <summary>
        /// Converts a string to nullable integer.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static int? ToNullableInt(this string s)
        {
            int result;
            if (int.TryParse(s, out result))
            {
                return result;
            }

            return null;
        }

        /// <summary>
        /// Converts a string to nullable integer 64.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static long? ToNullableLong(this string s)
        {
            long result;
            if (long.TryParse(s, out result))
            {
                return result;
            }

            return null;
        }

        /// <summary>
        /// Converts a string to GUID.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static Guid ToGuid(this string s)
        {
            Guid i = Guid.Empty;

            try
            {
                i = new Guid(s);
            }
            catch (Exception)
            {
                i = Guid.Empty;
            }

            return i;
        }

        /// <summary>
        /// Whether a child string is subset of parent string.
        /// </summary>
        /// <param name="strChild"></param>
        /// <param name="strParent"></param>
        /// <returns></returns>
        public static bool IsSubsetOf(this string strChild, string strParent)
        {
            if (string.IsNullOrEmpty(strParent))
            {
                return string.IsNullOrEmpty(strChild) ? true : false;
            }

            if (string.IsNullOrEmpty(strChild))
            {
                return true;
            }

            return strParent.ToLower().Contains(strChild.ToLower());
        }

        /// <summary>
        /// If the value is null, the Contains statement is return true
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool ContainsTrueIfNull(this IEnumerable<int> source, string value)
        {
            if (string.IsNullOrEmpty(value)
                || value.ToInt() == 0)
            {
                return true;
            }
            else
            {
                return source.Contains(value.ToInt());
            }
        }

        public static bool ContainsTrueIfNull(this IEnumerable<int> source, int? value)
        {
            if (!value.HasValue)
            {
                return true;
            }
            else
            {
                return source.Contains(value.Value);
            }
        }

        //public static string ToYesNo(this bool? boolean)
        //{
        //    if (boolean == true)
        //    {
        //        return CommonConstants.YES;
        //    }
        //    else if (boolean == false)
        //    {
        //        return CommonConstants.NO;
        //    }
        //    else
        //    {
        //        return string.Empty;
        //    }
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="b"></param>
        /// <param name="yes"></param>
        /// <param name="no"></param>
        /// <returns></returns>
        public static string ToString(this bool? b, string yes, string no)
        {
            return b.HasValue ? b.Value ? yes : no : string.Empty;
        }

        /// <summary>
        /// Converts a list to string. Each item is separated by a separator.
        /// </summary>
        /// <param name="list"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        public static string ToString(this List<int> list, string separator)
        {
            if (list == null || list.Count == 0)
            {
                return string.Empty;
            }

            StringBuilder sb = new StringBuilder();
            foreach (int item in list)
            {
                sb.AppendFormat("{0}{1}", item.ToString(), separator);
            }

            // remove last separator
            string result = sb.ToString().TrimEnd(separator.ToCharArray());

            return result;
        }

        /// <summary>
        /// Converts a list to string to a single string. Each item is separated by a separator.
        /// </summary>
        /// <param name="list"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        public static string ToString(this List<string> list, string separator)
        {
            if (list == null || list.Count == 0)
            {
                return string.Empty;
            }

            StringBuilder sb = new StringBuilder();
            foreach (string item in list)
            {
                if (!string.IsNullOrEmpty(item))
                {
                    sb.AppendFormat("{0}{1}", item, separator);
                }
            }

            // remove last separator
            string result = sb.ToString().TrimEnd(separator.ToCharArray());

            return result;
        }

        public static string ToString(this bool? b, string yes, string no, string nullOrEmpty)
        {
            return b.HasValue ? b.Value ? yes : no : nullOrEmpty;
        }

        public static string CalculateAge(this DateTime? birthDate)
        {
            if (birthDate == null)
            {
                return string.Empty;
            }

            // cache the current time
            DateTime now = DateTime.Today; // today is fine, don't need the timestamp from now

            // get the difference in years
            int years = now.Year - birthDate.Value.Year;

            // subtract another year if we're before the
            // birth day in the current year
            if (now.Month < birthDate.Value.Month || (now.Month == birthDate.Value.Month && now.Day < birthDate.Value.Day))
            {
                --years;
            }

            if (years > 1)
            {
                return years.ToString() + " Years";
            }
            else
            {
                return years.ToString() + " Year";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="o"></param>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        public static void SetProperty(this object o, string propertyName, object value)
        {
            PropertyInfo propInfo = o.GetType()
                                        .GetProperties()
                                        .Where(r => r.Name.ToLower() == propertyName.ToLower())
                                        .FirstOrDefault();
            if (propInfo != null
                && propInfo.CanWrite)
            {
                propInfo.SetValue(o, value, null);
            }
        }

        public static object GetProperty(this object o, string propertyName)
        {
            PropertyInfo propInfo = o.GetType()
                                        .GetProperties()
                                        .Where(r => r.Name.ToLower() == propertyName.ToLower())
                                        .FirstOrDefault();

            if (propInfo != null && propInfo.CanRead)
            {
                return propInfo.GetValue(o, null);
            }

            return null;
        }

        //public static string ToCapitalizationString(this string str)
        //{
        //    return string.Format(new LafiProperCaseFormatProvider(), "{0:mc}", str);
        //}

        public static string Reduce(this string str, int count)
        {
            if (!string.IsNullOrEmpty(str)
                && str.Length > count)
            {
                return str.Substring(0, count);
            }
            else
                return str;
        }

        /// <summary>
        /// Check if the date is valid against SQL datetime range.
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns>true if DateTime value is valid, otherwise false.</returns>
        public static bool IsValidSqlDateTime(this DateTime? value)
        {
            if (value != null)
            {
                try
                {
                    SqlDateTime.Parse(value.ToString());
                }
                catch (SqlTypeException)
                {
                    return false;
                }
            }

            return true;
        }

        public static DateTime ToMinIfNull(this DateTime? dt)
        {
            return dt.HasValue ? dt.Value : DateTime.MinValue;
        }

        public static DateTime? ToNullIfMin(this DateTime dt)
        {
            return dt == DateTime.MinValue ? new DateTime?() : dt;
        }

        public static bool IsNullOrInFuture(this DateTime? dt)
        {
            return !dt.HasValue
                        || dt.Value.Date > DateTime.Now.Date;
        }        

        public static string ToShortDateString(this DateTime? dt)
        {
            if (dt.HasValue)
            {
                return dt.Value.ToString("MM/dd/yyyy");
            }

            return dt.ToShortDateString(string.Empty);
        }

        public static string ToShortDateString(this DateTime? dt, string valueIfNull)
        {
            if (dt.HasValue)
            {
                return dt.Value.ToString("MM/dd/yyyy");
            }

            return valueIfNull;
        }

        public static string ToString(this DateTime? dt, string format)
        {
            if (dt.HasValue)
            {
                return dt.Value.ToString(format);
            }

            return string.Empty;
        }

        public static bool ToBool(this bool? value)
        {
            return value.HasValue ? value.Value : false;
        }

        public static string DigitOnly(this string textValue)
        {
            if (String.IsNullOrEmpty(textValue))
            {
                return string.Empty;
            }

            string result = string.Empty;
            foreach (char item in textValue.ToCharArray())
            {
                if (Char.IsDigit(item))
                {
                    result += item;
                }
            }

            return result;
        }

        public static string FormatPhone(this string textValue)
        {
            if (String.IsNullOrEmpty(textValue))
            {
                return String.Empty;
            }

            if (textValue.DigitOnly().Length == 10)
            {
                return String.Format("{0:(###) ###-####}", Int64.Parse(textValue.DigitOnly()));
            }
            else
            {
                return textValue;
            }
        }

        /// <summary>
        /// Perform "Like" search in a string.
        /// </summary>
        /// <param name="soureString">String </param>
        /// <param name="searchString">String </param>
        /// <returns></returns>
        public static bool Like(this string soureString, string searchString)
        {
            if (string.IsNullOrEmpty(soureString) || string.IsNullOrEmpty(searchString))
            {
                return false;
            }

            var n = soureString.IndexOf(searchString);
            return n != -1;
        }

        /// <summary>
        /// Perform "Like" search with case seneitive.
        /// </summary>
        /// <param name="soureString"></param>
        /// <param name="searchString"></param>
        /// <param name="caseSensitive"></param>
        /// <returns></returns>
        public static bool Like(this string soureString, string searchString, bool caseSensitive)
        {
            if (string.IsNullOrEmpty(soureString) || string.IsNullOrEmpty(searchString))
            {
                return false;
            }

            if (caseSensitive)
            {
                soureString = soureString.ToUpper();
                searchString = searchString.ToUpper();
            }

            var n = soureString.IndexOf(searchString);
            return n != -1;
        }

        /// <summary>
        /// CombinedKey has format "1:000000-000-000-00000"
        /// </summary>
        /// <param name="s">should be integer string</param>
        /// <param name="guidKey">should be guid object</param>
        /// <returns></returns>
        public static string ToCombinedKey(this string s, object guidKey)
        {
            if (String.IsNullOrEmpty(s))
            {
                return String.Empty;
            }

            return s.ToInt().ToString() + ":" + guidKey.ToString().ToGuid().ToString();
        }

        public static int GetKey(this string s)
        {
            if (string.IsNullOrEmpty(s)
                || string.IsNullOrEmpty(s.Trim()))
            {
                return 0;
            }

            string[] arr = s.Trim().Split(':');
            if (arr.Length >= 1)
            {
                return arr[0].ToInt();
            }
            else
            {
                return 0;
            }
        }

        public static Guid GetGuidKey(this string s)
        {
            if (string.IsNullOrEmpty(s)
                || string.IsNullOrEmpty(s.Trim()))
            {
                return Guid.Empty;
            }

            string[] arr = s.Trim().Split(':');
            if (arr.Length >= 2)
            {
                return arr[1].ToGuid();
            }
            else
            {
                return Guid.Empty;
            }
        }

        public static T Min<T, TCompare>(this IEnumerable<T> collection, Func<T, TCompare> func) where TCompare : IComparable<TCompare>
        {
            T item = collection.OrderBy(func).FirstOrDefault();
            return item;
        }

        public static T Max<T, TCompare>(this IEnumerable<T> collection, Func<T, TCompare> func) where TCompare : IComparable<TCompare>
        {
            T item = collection.OrderByDescending(func).FirstOrDefault();
            return item;
        }        

        /// <summary>
        /// Checks whether a string contains another string with ignore case sensitive option.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="searchValue"></param>
        /// <param name="ignoreCaseSensitive"></param>
        /// <returns></returns>
        public static bool Contains(this string source, string searchValue, bool ignoreCaseSensitive)
        {
            if (string.IsNullOrEmpty(source))
            {
                return false;
            }

            if (string.IsNullOrEmpty(searchValue))
            {
                return true;
            }

            if (ignoreCaseSensitive)
            {
                return source.IndexOf(searchValue, StringComparison.InvariantCultureIgnoreCase) > -1;
            }
            else
            {
                return source.Contains(searchValue);
            }
        }        

        /// <summary>
        /// Converts a nullable decimal value to string.
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        public static string ToStringInt(this decimal? d)
        {
            return d.HasValue ? ((int)d.Value).ToString() : string.Empty;
        }

        /// <summary>
        /// Converts a nullable decimal value to string.
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        public static string ToStringDecimal(this decimal? d)
        {
            return d.HasValue ? d.Value.ToString(".00").Split('.')[1] : string.Empty;
        }

        /// <summary>
        /// Gets nullable decimal value. Returns 0 if it is null.
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        public static decimal ValueOrZero(this decimal? d)
        {
            return d.HasValue ? d.Value : 0;
        }

        /// <summary>
        /// Convert decimal valut to US currentcy string.
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        public static string ToCurrencyString(this decimal? d)
        {
            return d.HasValue ? d.Value.ToString("C") : string.Empty;
        }

        /// <summary>
        /// Removes last matched string.
        /// </summary>
        /// <param name="text"></param>
        /// <param name="trimString"></param>
        /// <returns></returns>
        public static string TrimEnd(this string text, string trimString)
        {
            int lastIndex = text.LastIndexOf(trimString);
            if (lastIndex > 0)
            {
                text = text.Remove(lastIndex);
            }

            return text;
        }
    }
}
